#ifndef AST_H
#define AST_H

#include <list>
#include <map>
#include <string>
#include <utility>

enum AstNodeType
{
    ArcExpression,
    Bool,
    BoolWithConstraints,
    CodeBlock,
    ColsetDeclaration,
    Connection,
    Constraints,
    Declaration,
    Enum,
    EnumWithConstraints,
    Identifier,
    Int,
    IntWithConstraints,
    MultisetValue,
    Node,
    OpEqual,
    OpGreaterThan,
    OpGreaterThanEqual,
    OpLesserThan,
    OpLesserThanEqual,
    OpNotEqual,
    Place,
    PlaceParams,
    Statement,
    String,
    StringWithConstraints,
    Transition,
    TransitionParams,
    Type,
    Unit,
    UnitWithConstraints,
    Value,
    ValueDeclaration,
    VariableDeclaration
};

enum Operator
{
    OperatorEqual,
    OperatorNotEqual,
    OperatorLesserThan,
    OperatorGreaterThan,
    OperatorLesserThanEqual,
    OperatorGreaterThanEqual
};

enum Direction
{
    PlaceToTransition,
    TransitionToPlace
};

class AstNode
{
public:
    virtual ~AstNode() {}

public:
    virtual AstNodeType nodeType() const = 0;
    virtual std::string stringValue() const = 0;
};

class AstValue : public AstNode
{
public:
    AstValue(const std::string& value);
    virtual ~AstValue() {}

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const; // usable
    virtual AstValue* copy();

private:
    std::string m_value;

};

class AstMultisetValue : public AstValue
{
public:
    AstMultisetValue();
    AstMultisetValue(const AstMultisetValue& other);

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const; // usable
    virtual AstValue* copy();

    void addValue(const std::string& mul, const std::string& value);
    std::list<std::pair<std::string, std::string> > values() const;

private:
    std::list<std::pair<std::string, std::string> > m_values;
};

class AstIdentifier : public AstNode
{
public:
    AstIdentifier(const std::string& identifier);

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const; // usable
    virtual AstIdentifier* copy();

private:
    std::string m_identifier;
};

class AstType : public AstNode
{
public:
    ~AstType() {}

public:
    virtual AstNodeType nodeType() const = 0;
    virtual std::string stringValue() const = 0;
    virtual AstType* copy() = 0;
};

class AstBool : public AstType
{
public:
    ~AstBool() {}

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstType* copy();
};

class AstBoolWithConstraints : public AstBool
{
public:
    AstBoolWithConstraints();
    AstBoolWithConstraints(const std::list<std::string>& constraints);

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstType* copy();

    void addConstraint(const std::string& constraint);
    std::list<std::string> constraints() const;

private:
    std::list<std::string> m_constraints;
};

class AstInt : public AstType
{
public:
    ~AstInt() {}

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstType* copy();
};

class AstIntWithConstraints : public AstInt
{
public:
    AstIntWithConstraints();
    AstIntWithConstraints(const std::list<std::string>& constraints);

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstType* copy();

    void addConstraint(const std::string& constraint);
    std::list<std::string> constraints() const;

private:
    std::list<std::string> m_constraints;
};

class AstString : public AstType
{
public:
    ~AstString() {}

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstType* copy();
};

class AstStringWithConstraints : public AstString
{
public:
    AstStringWithConstraints();
    AstStringWithConstraints(const std::list<std::string>& constraints);

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstType* copy();

    void addConstraint(const std::string& constraint);
    std::list<std::string> constraints() const;

private:
    std::list<std::string> m_constraints;
};

class AstUnit : public AstType
{
public:
    ~AstUnit() {}

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstType* copy();
};

class AstUnitWithConstraints : public AstUnit
{
public:
    AstUnitWithConstraints();
    AstUnitWithConstraints(const std::list<std::string>& constraints);

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstType* copy();

    void addConstraint(const std::string& constraint);
    std::list<std::string> constraints() const;

private:
    std::list<std::string> m_constraints;
};

class AstEnum : public AstType
{
public:
    ~AstEnum() {}

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstType* copy();
};

class AstEnumWithConstraints : public AstEnum
{
public:
    AstEnumWithConstraints();
    AstEnumWithConstraints(const std::list<std::string>& constraints);

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstType* copy();

    void addConstraint(const std::string& constraint);
    std::list<std::string> constraints() const;

private:
    std::list<std::string> m_constraints;
};

class AstStatement : public AstNode
{
public:
    virtual ~AstStatement() {}

public:
    virtual AstNodeType nodeType() const = 0;
    virtual std::string stringValue() const = 0;
    virtual AstStatement* copy() = 0;
};

class AstDeclaration : public AstStatement
{
public:
    virtual ~AstDeclaration() {}

public:
    virtual AstNodeType nodeType() const = 0;
    virtual std::string stringValue() const = 0;

    virtual const AstIdentifier& identifier() const = 0;
};

class AstValueDeclaration : public AstDeclaration
{
public:
    AstValueDeclaration(AstIdentifier* identifier,
        AstValue* value);
    AstValueDeclaration(AstValueDeclaration& other);
    AstValueDeclaration& operator=(const AstValueDeclaration& other);
    ~AstValueDeclaration();

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstStatement* copy();

    virtual const AstIdentifier& identifier() const;
    const AstValue& value() const;

private:
    AstIdentifier* m_identifier;
    AstValue* m_value;
};

class AstVariableDeclaration : public AstDeclaration
{
public:
    AstVariableDeclaration(AstIdentifier* identifier,
        AstType* type);
    AstVariableDeclaration(AstVariableDeclaration& other);
    AstVariableDeclaration& operator=(const AstVariableDeclaration& other);
    ~AstVariableDeclaration();

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstStatement* copy();

    virtual const AstIdentifier& identifier() const;
    const AstType& type() const;

private:
    AstIdentifier* m_identifier;
    AstType* m_type;
};

class AstColsetDeclaration : public AstDeclaration
{
public:
    AstColsetDeclaration(AstIdentifier* identifier,
        AstType* type);
    AstColsetDeclaration(AstColsetDeclaration& other);
    AstColsetDeclaration& operator=(const AstColsetDeclaration& other);
    ~AstColsetDeclaration();

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstStatement* copy();

    virtual const AstIdentifier& identifier() const;
    const AstType& type() const;

private:
    AstIdentifier* m_identifier;
    AstType* m_type;
};

class AstOperator : public AstNode
{
public:
    AstOperator(Operator op);

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstOperator* copy();

private:
    Operator m_operator;
};

class AstPlaceParams : public AstNode
{
public:
    AstPlaceParams(AstType* colset, const std::string& name = "",
        AstValue* marking = 0);
    AstPlaceParams(const AstPlaceParams& other);
    AstPlaceParams& operator=(const AstPlaceParams& other);
    ~AstPlaceParams();

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstPlaceParams* copy();

    const AstType& colset() const;
    std::string name() const;
    const AstValue& marking() const;

private:
    AstType* m_colset;
    std::string m_name;
    AstValue* m_marking;
};

class AstTransitionParams : public AstNode
{
public:
    AstTransitionParams(const std::string& loperand, AstOperator* op,
        const std::string& roperand);
    AstTransitionParams(const std::string& loperand, AstOperator* op,
        AstValue* roperand);
    AstTransitionParams(const AstTransitionParams& other);
    AstTransitionParams& operator=(const AstTransitionParams& other);
    ~AstTransitionParams();

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstTransitionParams* copy();

    std::string leftOperand() const;
    std::string rightOperand() const;
    const AstValue& rightOperandValued() const;
    const AstOperator& op() const;

    bool isEmpty() const;
    bool isRightOperandValued() const;

private:
    std::string m_loperand;
    std::string m_roperand;
    AstValue* m_roperandValued;
    AstOperator* m_op;
};

class AstPlace : public AstNode
{
public:
    AstPlace(const std::string& id, AstPlaceParams* params, bool createNew);
    AstPlace(const AstPlace& other);
    AstPlace& operator=(const AstPlace& other);
    ~AstPlace();

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstPlace* copy();

    std::string id() const;
    const AstPlaceParams& params() const;
    bool createNew() const;

private:
    std::string m_id;
    AstPlaceParams* m_params;
    bool m_createNew;
};

class AstTransition : public AstNode
{
public:
    AstTransition(const std::string& id, AstTransitionParams* params,
        bool createNew);
    AstTransition(const AstTransition& other);
    AstTransition& operator=(const AstTransition& other);
    ~AstTransition();

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstTransition* copy();

    std::string id() const;
    const AstTransitionParams& params() const;
    bool createNew() const;

private:
    std::string m_id;
    AstTransitionParams* m_params;
    bool m_createNew;
};

class AstArcExpression : public AstNode
{
public:
    AstArcExpression(AstValue* value);
    AstArcExpression(const AstArcExpression& other);
    AstArcExpression& operator=(const AstArcExpression& other);
    ~AstArcExpression();

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstArcExpression* copy();

    const AstValue& value() const;

private:
    AstValue* m_value;
};

class AstConnection : public AstStatement
{
public:
    AstConnection(AstPlace* place, AstArcExpression* arcExpression,
        AstTransition* transition);
    AstConnection(AstTransition* transition, AstArcExpression* arcExpression,
        AstPlace* place);
    AstConnection(const AstConnection& other);
    AstConnection& operator=(const AstConnection& other);
    ~AstConnection();

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;
    virtual AstStatement* copy();

    const AstPlace& place() const;
    const AstTransition& transition() const;
    const AstArcExpression& arcExpression() const;
    Direction direction() const;

private:
    AstPlace* m_place;
    AstTransition* m_transition;
    AstArcExpression* m_arcExpression;
    Direction m_direction;
};

class AstCodeBlock : public AstNode
{
public:
    AstCodeBlock();
    AstCodeBlock(AstCodeBlock& other);
    AstCodeBlock& operator=(const AstCodeBlock& other);
    ~AstCodeBlock();

public:
    virtual AstNodeType nodeType() const;
    virtual std::string stringValue() const;

    void addStatement(AstStatement* statement);
    void insertStatement(int index, AstStatement* statement);
    std::list<AstStatement*> statements() const;

private:
    std::list<AstStatement*> m_statements;
};

class AstError
{
public:
    int Line;
    int Column;
    std::string LexError;
    std::string ParseError;
};

class AstInfo
{
public:
    static AstInfo* i();
    static void destroy();

public:
    void astHandleError(const std::string& s);
    void astHandleLexError(const std::string& s);

    void handleNewLine();
    void handleColumnShift();

    std::list<AstError> errors() const;

private:
    AstInfo();
    ~AstInfo();

private:
    friend class SymbolTable;
    int m_currentLine;
    int m_currentColumn;
    std::list<AstError> m_errors;
};

class SymbolTable
{
    class SymbolInfo
    {
    public:
        AstStatement* Statement;
        int Column;
        int Line;
    };

public:
    static SymbolTable* i();
    static void destroy();

public:
    bool addSymbol(const std::string& id, AstStatement* stmt);
    bool lookupsymbol(const std::string& id);

private:
    SymbolTable();
    ~SymbolTable();

private:
    std::map<std::string, SymbolInfo> m_symbols;
};

#endif
